"""Low-level helpers for LDAP credential management."""

import functools
import typing

from cki_lib import logger
import ldap

LOGGER = logger.get_logger(__name__)


def ldap_clean_attributes(
    ldap_server: str,
    attributes: dict[str, list[bytes]],
) -> dict[str, str | list[str]]:
    """Clean the LDAP server response.

    The following changes are made:
    - all (binary) values are decoded as UTF-8
    - dynamic Rover groups that can be detected by the existence of the
      rhatRoverGroupMemberQuery attribute on the group are removed from memberOf
    - single-value lists are reduced to the value itself
    """
    result: dict[str, str | list[str]] = {}
    for key, value in attributes.items():
        decoded_value = [v.decode('utf8') for v in value]
        if key == 'memberOf':
            decoded_value = [
                v for v in decoded_value
                if not ldap_search(ldap_server, v, attrlist=('rhatRoverGroupMemberQuery',))
            ]
        result[key] = decoded_value[0] if len(decoded_value) == 1 else decoded_value
    return result


@functools.cache
def ldap_search(ldap_server: str, dn: str, **kwargs: typing.Any) -> dict[str, str | list[str]]:
    """Query an LDAP server for an object."""
    LOGGER.debug('Querying %s for %s', ldap_server, dn)
    return ldap_clean_attributes(
        # pylint: disable=no-member
        ldap_server, ldap.initialize(ldap_server).search_s(dn, ldap.SCOPE_BASE, **kwargs)[0][1]
    )


def ldap_login(ldap_server: str, dn: str, password: str) -> None:
    """Query an LDAP server for an object."""
    LOGGER.debug('Logging into %s with %s', ldap_server, dn)
    ldap_object = ldap.initialize(ldap_server)
    ldap_object.start_tls_s()
    ldap_object.simple_bind_s(dn, password)
